/*
 * @描述: 
 * @创建者: kilomChou
 * @Date: 2021-04-11 17:21:46
 * @最后编辑: kilomChou
 * @LastEditTime: 2021-10-27 16:53:39
 */
import SnakeNode from "./SnakeNode";
import Food, { FoodType, IFoodInit } from "../food/Food";
import { RandUtil } from "../../util/RandUtil";
import World from "../world/World";
import { Util } from "../../util/Util";
import { GameEvent } from "../event/GameEvent";
import { XSnake } from "../../data/XlsxVo";
import { IAngCircle, IAngPos, Vec2 } from "../../core/vec2/Vec2";
import Vec2Operator from "../../core/vec2/Vec2Operator";
import SnakeAI from "./SnakeAI";
const [util, ru, v2, v2op] = [Util, RandUtil, Vec2, new Vec2Operator()];

/** 蛇类 */
export default class Snake {

    constructor(
        /** 所在世界 */
        readonly world: World,
        /** 唯一标识 */
        readonly id: number,
        name: string
    ) {
        this.name = name;
        this._respawn_key = "snake_respawn" + this.id;
    }

    /** 节点数组 */
    readonly nodes: SnakeNode[] = [];
    /** 蛇AI */
    readonly ai = new SnakeAI(this);

    /** 名称 */
    name: string;
    /** 头部节点 */
    head: SnakeNode;
    /** 得分 */
    score: number;
    /** 颜色 */
    colors: string[];
    /** 颜色整型 */
    rgbs: number[];
    /** 目标角度 */
    toAng: number;
    /** 保存点路径 */
    savePath: IAngPos[];
    /** 移动速度 */
    moveSpd: number;
    /** 转弯速度 */
    turnSpd: number;
    /** 与保存点的距离 */
    saveDis: number;
    /** 节点间距 */
    spacing: number;

    /** 存活状态 */
    isAlive: Boolean;
    /** 重生状态 */
    isRespawning: Boolean;
    /** 冲刺状态 */
    isSprint: Boolean;

    /** 定时器 */
    get timer() { return this.world.timer; }
    /** 尾部节点 */
    get tail() { return this.nodes[this.nodes.length - 1] }
    /** 游戏事件 */
    get ed() { return this.world.ed }

    /** 当前冲刺倍率 */
    private _sprintScale: number = 1;
    /** 重生定时器key */
    private _respawn_key: string;
    /** 撞到的物体 */
    private _collision: any

    /** 生成 */
    spawn() {
        //重置状态
        this.isAlive = true;
        this.isRespawning = false;
        this.isSprint = false;
        this.score = 0;
        this.saveDis = 0;

        //颜色
        this.colors = [ru.randColor()];
        while (ru.chance(0.25)) {
            this.colors.push(ru.randColor());
        }
        this.rgbs = [];
        this.colors.forEach(color => this.rgbs.push(util.color2rgb(color)));

        //节点
        this.clearNode();
        let { x, y } = this.world.getSpawnPos();
        //生成头部节点
        this.head = this.world.snakeMgr.createSnakeNode({
            x, y,
            r: XSnake.vo.initWidth * 0.5,
            ang: ru.rand(0, 360),
            snake: this,
            idx: -1
        });
        this.toAng = this.head.ang;
        this.setWidth();
        this.nodes.push(this.head);
        this.savePath = [v2.cpAngPos(this.head)];
        this.head.updateChunks();
        ///生成身体节点
        for (let i = XSnake.vo.initLen - 1; i >= 0; i--) {
            this.addNode();
        }
        //发送事件
        this.ed.send(GameEvent.SNAKE_SPAWN, this);
    }

    /** 每帧执行 */
    update(order: number) {
        switch (order) {
            case 0:
                if (this.isAlive) {
                    this.move();
                    this.eatFood();
                    this.grow();
                }
                break;
            case 1:
                if (this.isAlive) {
                    this.collideCheck();
                }
                break;
            case 2:
                if (this.isAlive) {
                    this.collide();
                    this.ai.update();
                } else if (this.isRespawning) {
                    this.respawn();
                }
                break;
        }
    }

    // ============ 帧行为 ============

    /** 移动 */
    move() {
        let dt = this.world.dt;
        if (this.isSprint && this.score > XSnake.vo.sprintCost) {
            //冲刺加速
            this._sprintScale += XSnake.vo.sprintSpd * (dt / XSnake.vo.sprintSpdTime);
            //消耗分数
            this.score -= XSnake.vo.sprintCost * dt;
        } else {
            //减速
            this._sprintScale -= 2 * XSnake.vo.sprintSpd * (dt / XSnake.vo.sprintSpdTime);
        }
        this._sprintScale = util.range(this._sprintScale, 1, XSnake.vo.sprintSpd);


        //计算移动距离和转弯角度
        let moveDis = this.getMoveDis();
        let turnAngle = this.getTurnAngle();

        //转弯移动
        this.head.op.oAng(turnAngle).moveForward(moveDis);

        //更新保存点距离
        this.saveDis += moveDis;
        if (this.saveDis > this.spacing) {
            //记录保存点
            let count = Math.floor(this.saveDis / this.spacing);
            this.saveDis %= this.spacing;
            while (count-- > 0) {
                let savePos = this.savePath.pop();
                v2op.setObj(savePos).angPosAs(this.head).moveForward(-this.saveDis - this.spacing * count);
                this.savePath.unshift(savePos);
            }
        }
        this.head.updateChunks();
        for (let i = 1, len = this.nodes.length; i < len; i++) {
            this.nodes[i].move();
        }
    }

    /** 进食 */
    eatFood() {
        //进食碰撞器
        let eatCol = v2op.setCirObj(tmpAngCir).posAs(this.head).setR(this.head.r * XSnake.vo.eatRange);
        let chunks = this.world.getChunks(tmpAngCir);
        let foodSet = new Set<Food>();

        //遍历区块中的食物
        this.world.foodMgr.eachFoods(chunks, food => {
            if (eatCol.isColCir(food)) {
                //即将被吃的食物
                foodSet.add(food);
            }
        })
        foodSet.forEach(food => {
            //进食加分
            this.score += food.score;
            food.eaten(this.head.id);
        })
    }

    /** 成长 */
    grow() {
        this.setWidth();
        let len = this.getNodeCount() - this.nodes.length;

        while (len > 0) {
            len--;
            this.addNode();
        }
        while (len < 0) {
            len++;
            this.popNode();
        }
    }

    /** 碰撞检测 */
    collideCheck() {
        this._collision = null;
        if (this.head.isColWall) {
            this._collision = this.world;
        } else {
            //头部碰撞器
            let headCol = v2op.setCirObj(tmpAngCir).angCirAs(this.head).moveForward(this.head.r * 1.25);
            let chunks = this.world.getChunks(this.head);

            //遍历区块中的蛇节点
            this.world.snakeMgr.eachSnakeNodeSetExclude(chunks, this, node => {
                //取交集
                if (headCol.isColCir(node) && this.head.op.isColCir(node)) {
                    this._collision = node;
                    return true;
                }
            })
        }
    }

    /** 碰撞 */
    collide() {
        if (this._collision) {
            this.die(this._collision);
            this._collision = null;
        }
    }

    /** 重生 */
    respawn() {
        this.world.timer.check(this._respawn_key, () => {
            this.spawn();
        })
    }

    // ============ 方法 ============

    /** 增长节点 */
    addNode() {
        let node = this.world.snakeMgr.createSnakeNode(this.tail);
        //设置保存点
        this.savePath.push(v2.cpAngPos(node));
        this.nodes.push(node);
    }

    /** 缩短节点 */
    popNode() {
        this.world.snakeMgr.removeSnakeNode(this.nodes.pop());
    }

    /** 清空节点 */
    clearNode() {
        for (let i = this.nodes.length - 1; i >= 0; i--) {
            this.popNode();
        }
    }

    /** 死亡 */
    die(killer: any) {
        //设置状态
        this.isAlive = false;
        this.isRespawning = true;
        //生成战利品食物
        let score = (ru.rand2(XSnake.vo.deathScore) + this.score / 2.33) / this.nodes.length;
        let range = this.head.w / 3.33;
        this.nodes.forEach(node => {
            v2.setPos(tmpFoodInit,
                ru.rand(node.x - range, node.x + range),
                ru.rand(node.y - range, node.y + range),
            );
            tmpFoodInit.color = this.colors[node.idx % this.colors.length];
            tmpFoodInit.score = ru.rand(score * 0.66, score * 1.66);
            this.world.foodMgr.createFood(tmpFoodInit);
        })
        //清空节点
        this.clearNode();
        //设置定时器
        this.timer.set(this._respawn_key, XSnake.vo.respawnTime);
    }

    /** 设置宽度 */
    setWidth(w: number = this.getWidth()) {
        this.nodes.forEach(node => {
            node.w = w;
        })
        this.moveSpd = this.getMoveSpd();
        this.turnSpd = this.getTurnSpd();
        this.spacing = this.getSpacing();
    }

    // ============ 公式 ============

    /** 体型宽度 */
    getWidth() {
        return XSnake.vo.initWidth + Math.pow(this.score, 0.33);
    }

    /** 节点个数 */
    getNodeCount() {
        let count = this.score / this.head.w;
        if (count > 111) {
            count = 111 + (count - 111) / 1.11;
            if (count > 222) {
                count = 222 + (count - 222) / 1.22;
                if (count > 333) {
                    count = 333 + (count - 333) / 1.33;
                    if (count > 666) {
                        count = 666 + (count - 666) / 6.66;
                        if (count > 888) {
                            count = 888 + (count - 888) / 8.88;
                        }
                    }
                }
            }
        }
        return XSnake.vo.initLen + Math.floor(count);
    }

    /** 蛇长度米数 */
    getMeter() {
        return Math.floor(this.nodes.length * this.head.r);
    }

    /** 每秒移动距离 */
    getMoveSpd() {
        return XSnake.vo.initWidth * 8.88 + Math.pow(this.head.w - XSnake.vo.initWidth, 0.66) * 3.33;
    }

    /** 每秒转弯角度 */
    getTurnSpd() {
        let dis = (XSnake.vo.turnWidth * this.head.w * Math.PI) / 360
        return this.moveSpd / dis
    }
    /** 每帧移动距离 */
    getMoveDis() {
        return this.moveSpd * this._sprintScale * this.world.dt;
    }

    /** 每帧转弯角度 */
    getTurnAngle() {
        let angle = this.turnSpd * this.world.dt;
        let diff = v2.angDiff(this.head.ang, this.toAng);
        let sign = Math.sign(diff);
        return angle > Math.abs(diff) ? diff : sign * angle;
    }

    /** 获取蛇节点之间的间隔 */
    getSpacing() {
        return this.head.w * XSnake.vo.initSpacing - Math.pow(this.head.w - XSnake.vo.initWidth, 0.88);
    }

}

const tmpAngCir: IAngCircle = { x: 0, y: 0, r: 0, ang: 0 };
const tmpFoodInit: IFoodInit = { x: 0, y: 0, r: 0, ang: 0, type: FoodType.snake };